#include "StdAfx.h"
#include "NULL_Renderer.h"
#include "NULLRenderAuxGeom.h"

CNULLRenderAuxGeom* CNULLRenderAuxGeom::s_pThis = NULL;

#pragma warning(disable: 4244)

#ifdef ENABLE_WGL_DEBUG_RENDERER

static const float W = 800.0f;
static const float H = 600.0f;
static const float THETA = 5.0f;
static const Vec3 VUP(0.0f, 0.0f, 1.0f);

bool CNULLRenderAuxGeom::s_active = false;
bool CNULLRenderAuxGeom::s_hidden = true;

void CNULLRenderAuxGeom::EnableOpenGL()
{
	CCamera& camera = gEnv->pSystem->GetViewCamera();
	camera.SetFrustum(W, H);

	const float FOV = camera.GetFov() / PI * 180.0f;
	const float PNR = camera.GetNearPlane();
	const float PFR = camera.GetFarPlane();

	PIXELFORMATDESCRIPTOR pfd;
	int format;

	// get the device context (DC)
	m_hdc = GetDC( m_hwnd );

	// set the pixel format for the DC
	ZeroMemory( &pfd, sizeof( pfd ) );
	pfd.nSize = sizeof( pfd );
	pfd.nVersion = 1;
	pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
	pfd.iPixelType = PFD_TYPE_RGBA;
	pfd.cColorBits = 24;
	pfd.cDepthBits = 16;
	pfd.iLayerType = PFD_MAIN_PLANE;
	format = ChoosePixelFormat( m_hdc, &pfd );
	SetPixelFormat( m_hdc, format, &pfd );

	// create and enable the render context (RC)
	m_glrc = wglCreateContext( m_hdc );
	wglMakeCurrent( m_hdc, m_glrc );

	m_qobj = gluNewQuadric();

	glShadeModel(GL_FLAT);
	glPolygonMode(GL_FRONT, GL_FILL);
	glEnable(GL_DEPTH_TEST);

	glViewport(0, 0, W, H);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(FOV, W/H, PNR, PFR);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

void CNULLRenderAuxGeom::DisableOpenGL()
{
	gluDeleteQuadric(m_qobj);

	wglMakeCurrent( NULL, NULL );
	wglDeleteContext( m_glrc );
	ReleaseDC( m_hwnd, m_hdc );
}

#endif

CNULLRenderAuxGeom::CNULLRenderAuxGeom( CNULLRenderer& renderer )
: m_renderer( &renderer )
{
#ifdef ENABLE_WGL_DEBUG_RENDERER
	const char* wndClassName = "DebugRenderer";

	// register window class
	WNDCLASS wc;
	wc.style = CS_OWNDC;
	wc.lpfnWndProc = WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = GetModuleHandle(NULL);
	wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
	wc.hCursor = LoadCursor( NULL, IDC_ARROW );
	wc.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH );
	wc.lpszMenuName = NULL;
	wc.lpszClassName = wndClassName;
	RegisterClass( &wc );

	// create main window
	m_hwnd = CreateWindow( 
		wndClassName, wndClassName,
		WS_CAPTION | WS_POPUP,
		0, 0, W, H,
		NULL, NULL, wc.hInstance, NULL );

	ShowWindow(m_hwnd, SW_HIDE);
	UpdateWindow(m_hwnd);

	EnableOpenGL();

	m_eye.Set(0.0f, 0.0f, 0.0f);
	m_dir.Set(0.0f, 1.0f, 0.0f);
	m_up.Set(0.0f, 0.0f, 1.0f);

	REGISTER_COMMAND("r_debug_renderer_show_window", DebugRendererShowWindow, VF_NULL,"");
	REGISTER_COMMAND("r_debug_renderer_set_eye_pos", DebugRendererSetEyePos , VF_NULL,"");
#endif
}

CNULLRenderAuxGeom::~CNULLRenderAuxGeom()
{
#ifdef ENABLE_WGL_DEBUG_RENDERER
	DisableOpenGL();
	DestroyWindow(m_hwnd);
#endif
}

void CNULLRenderAuxGeom::BeginFrame()
{
#ifdef ENABLE_WGL_DEBUG_RENDERER
	FUNCTION_PROFILER(gEnv->pSystem, PROFILE_RENDERER);

	// check for messages
	MSG msg;
	// Must be PeekMessageW for Scaleform IME to function correctly and to ensure WM_CHAR contains
	// Unicode widechar for languages like Russian
	if ( PeekMessageW( &msg, NULL, 0, 0, PM_REMOVE )  )
	{
			TranslateMessage( &msg );
			DispatchMessage( &msg );
	} 

	{
		m_dir.normalize();
		m_up.normalize();

		Vec3 right = m_dir ^ m_up;

		if (s_active)
		{
			Matrix34 m;

			m.SetIdentity();
			if ( GetAsyncKeyState('W') & 0x8000 )
				m.AddTranslation(m_dir);
			if ( GetAsyncKeyState('S') & 0x8000 )
				m.AddTranslation(-m_dir);
			if ( GetAsyncKeyState('A') & 0x8000 )
				m.AddTranslation(-right);
			if ( GetAsyncKeyState('D') & 0x8000 )
				m.AddTranslation(right);
			m_eye = m * m_eye;

			m.SetIdentity();
			if ( GetAsyncKeyState(VK_RIGHT) & 0x8000 )
				m.SetRotationAA(-PI/180.0f*THETA, VUP); // !m_up
			if ( GetAsyncKeyState(VK_LEFT) & 0x8000 )
				m.SetRotationAA(PI/180.0f*THETA, VUP); // !m_up
			if ( GetAsyncKeyState(VK_UP) & 0x8000 )
				m.SetRotationAA(PI/180.0f*THETA, right);
			if ( GetAsyncKeyState(VK_DOWN) & 0x8000 )
				m.SetRotationAA(-PI/180.0f*THETA, right);
			m_up = m * m_up;
			m_dir = m * m_dir;
		}

		Matrix34 m( Matrix33::CreateOrientation(m_dir, m_up, 0), m_eye );
		gEnv->pSystem->GetViewCamera().SetMatrix(m);
	}
#endif
}

void CNULLRenderAuxGeom::EndFrame()
{
#ifdef ENABLE_WGL_DEBUG_RENDERER
	FUNCTION_PROFILER(gEnv->pSystem, PROFILE_RENDERER);

	if (!s_hidden)
	{
		glLoadIdentity();

		Vec3 at = m_eye + m_dir;
		gluLookAt(m_eye.x, m_eye.y, m_eye.z, at.x, at.y, at.z, m_up.x, m_up.y, m_up.z );

		glClearColor(0.0f, 0.0f, 0.3f, 0.0f);
		glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

		glColor3f(1.0f, 0.0f, 0.0f);
		gluSphere(m_qobj, 1.0f, 32, 32);

		glInterleavedArrays(GL_C3F_V3F, 0, &m_points[0]);
		glDrawArrays( GL_POINTS, 0, m_points.size() );

		glInterleavedArrays(GL_C3F_V3F, 0, &m_lines[0]);
		glDrawArrays( GL_LINES, 0, m_lines.size() * 2 );

		for (size_t i = 0; i < m_polyLines.size(); ++i)
		{
			const SPolyLine& polyline = m_polyLines[i];
			glInterleavedArrays(GL_C3F_V3F, 0, &polyline.points[0]);
			glDrawArrays( GL_LINE_STRIP, 0, polyline.points.size() );
		}

		glInterleavedArrays(GL_C3F_V3F, 0, &m_triangles[0]);
		glDrawArrays( GL_TRIANGLES, 0, m_triangles.size() * 3 );

		for (size_t i = 0; i < m_spheres.size(); ++i)
		{
			glColor3fv(m_spheres[i].p.color);
			glPushMatrix();
			glLoadIdentity();
			glTranslatef(m_spheres[i].p.vertex[0], m_spheres[i].p.vertex[1], m_spheres[i].p.vertex[2]);
			gluSphere(m_qobj, m_spheres[i].r, 32, 32);
			glPopMatrix();
		}

		glFlush();

		SwapBuffers(m_hdc);
	}

	m_points.resize(0);
	m_lines.resize(0);
	m_polyLines.resize(0);
	m_triangles.resize(0);
	m_spheres.resize(0);
#endif
}

void CNULLRenderAuxGeom::DrawPoint(const Vec3& v, const ColorB& col, uint8 size /* = 1  */)
{
#ifdef ENABLE_WGL_DEBUG_RENDERER
	m_points.push_back( SPoint(v, col) );
#endif
}

void CNULLRenderAuxGeom::DrawPoints(const Vec3* v, uint32 numPoints, const ColorB* col, uint8 size /* = 1  */)
{
#ifdef ENABLE_WGL_DEBUG_RENDERER
	for (uint32 i = 0; i < numPoints; ++i)
		m_points.push_back( SPoint(v[i], *col) );
#endif
}

void CNULLRenderAuxGeom::DrawPoints(const Vec3* v, uint32 numPoints, const ColorB& col, uint8 size /* = 1  */)
{
#ifdef ENABLE_WGL_DEBUG_RENDERER
	for (uint32 i = 0; i < numPoints; ++i)
		m_points.push_back( SPoint(v[i], col) );
#endif
}

void CNULLRenderAuxGeom::DrawLine(const Vec3& v0, const ColorB& colV0, const Vec3& v1, const ColorB& colV1, float thickness /* = 1::0f  */)
{
#ifdef ENABLE_WGL_DEBUG_RENDERER
	m_lines.push_back( SLine( SPoint(v0, colV0), SPoint(v1, colV1) ) );
#endif
}

void CNULLRenderAuxGeom::DrawLines(const Vec3* v, uint32 numPoints, const ColorB& col, float thickness /* = 1::0f  */)
{
#ifdef ENABLE_WGL_DEBUG_RENDERER
	assert( ( numPoints >= 2 ) && ( 0 == ( numPoints & 1 ) ) );
	for (uint32 i = 0; i < numPoints; i+=2)
		m_lines.push_back( SLine( SPoint(v[i], col), SPoint(v[i+1], col) ) );
#endif
}

void CNULLRenderAuxGeom::DrawLines(const Vec3* v, uint32 numPoints, const ColorB* col, float thickness /* = 1::0f  */)
{
#ifdef ENABLE_WGL_DEBUG_RENDERER
	assert( ( numPoints >= 2 ) && ( 0 == ( numPoints & 1 ) ) );
	for (uint32 i = 0; i < numPoints; i+=2)
		m_lines.push_back( SLine( SPoint(v[i], *col), SPoint(v[i+1], *col) ) );
#endif
}

void CNULLRenderAuxGeom::DrawLines(const Vec3* v, uint32 numPoints, const uint16* ind, uint32 numIndices, const ColorB& col, float thickness /* = 1::0f  */)
{
#ifdef ENABLE_WGL_DEBUG_RENDERER
	assert( numPoints >= 2 );
	assert( ( numIndices >= 2 ) && ( 0 == ( numIndices & 1 ) ) );
	for (uint32 i = 0; i < numIndices; i+=2)
	{
		uint16 i0 = ind[i], i1 = ind[i+1];
		assert(i0 < numPoints && i1 < numPoints);
		m_lines.push_back( SLine( SPoint(v[i0], col), SPoint(v[i1], col) ) );
	}
#endif
}

void CNULLRenderAuxGeom::DrawLines(const Vec3* v, uint32 numPoints, const uint16* ind, uint32 numIndices, const ColorB* col, float thickness /* = 1::0f  */)
{
#ifdef ENABLE_WGL_DEBUG_RENDERER
	assert( numPoints >= 2 );
	assert( ( numIndices >= 2 ) && ( 0 == ( numIndices & 1 ) ) );
	for (uint32 i = 0; i < numIndices; i+=2)
	{
		uint16 i0 = ind[i], i1 = ind[i+1];
		assert(i0 < numPoints && i1 < numPoints);
		m_lines.push_back( SLine( SPoint(v[i0], *col), SPoint(v[i1], *col) ) );
	}
#endif
}

void CNULLRenderAuxGeom::DrawPolyline(const Vec3* v, uint32 numPoints, bool closed, const ColorB& col, float thickness /* = 1::0f  */)
{
#ifdef ENABLE_WGL_DEBUG_RENDERER
	assert( numPoints >= 2 );
	assert( !closed || numPoints >= 3 ); // if "closed" then we need at least three vertices
	m_polyLines.resize(m_polyLines.size()+1);
	SPolyLine& polyline = m_polyLines[m_polyLines.size()-1];
	for (uint32 i = 0; i < numPoints; ++i)
		polyline.points.push_back( SPoint(v[i], col) );
	if (closed)
		polyline.points.push_back( SPoint(v[0], col) );
#endif
}

void CNULLRenderAuxGeom::DrawPolyline(const Vec3* v, uint32 numPoints, bool closed, const ColorB* col, float thickness /* = 1::0f  */)
{
#ifdef ENABLE_WGL_DEBUG_RENDERER
	assert( numPoints >= 2 );
	assert( !closed || numPoints >= 3 ); // if "closed" then we need at least three vertices
	m_polyLines.resize(m_polyLines.size()+1);
	SPolyLine& polyline = m_polyLines[m_polyLines.size()-1];
	for (uint32 i = 0; i < numPoints; ++i)
		polyline.points.push_back( SPoint(v[i], *col) );
	if (closed)
		polyline.points.push_back( SPoint(v[0], *col) );
#endif
}

void CNULLRenderAuxGeom::DrawTriangle(const Vec3& v0, const ColorB& colV0, const Vec3& v1, const ColorB& colV1, const Vec3& v2, const ColorB& colV2)
{
#ifdef ENABLE_WGL_DEBUG_RENDERER
	FUNCTION_PROFILER(gEnv->pSystem, PROFILE_RENDERER);
	m_triangles.push_back( STriangle( SPoint(v0, colV0), SPoint(v1, colV1), SPoint(v2, colV2) ) );
#endif
}

void CNULLRenderAuxGeom::DrawTriangles(const Vec3 *v, uint32 numPoints, const ColorB &col)
{
#ifdef ENABLE_WGL_DEBUG_RENDERER
	assert( ( numPoints >= 3 ) && ( 0 == ( numPoints % 3 ) ) );
	FUNCTION_PROFILER(gEnv->pSystem, PROFILE_RENDERER);
	for (size_t i = 0; i < numPoints; i+=3)
		m_triangles.push_back( STriangle( SPoint(v[i], col), SPoint(v[i+1], col), SPoint(v[i+2], col) ) );
#endif
}

void CNULLRenderAuxGeom::DrawTriangles(const Vec3 *v, uint32 numPoints, const ColorB *col)
{
#ifdef ENABLE_WGL_DEBUG_RENDERER
	assert( ( numPoints >= 3 ) && ( 0 == ( numPoints % 3 ) ) );
	FUNCTION_PROFILER(gEnv->pSystem, PROFILE_RENDERER);
	for (size_t i = 0; i < numPoints; i+=3)
		m_triangles.push_back( STriangle( SPoint(v[i], *col), SPoint(v[i+1], *col), SPoint(v[i+2], *col) ) );
#endif
}

void CNULLRenderAuxGeom::DrawTriangles(const Vec3 *v, uint32 numPoints, const uint16 *ind, uint32 numIndices, const ColorB &col)
{
#ifdef ENABLE_WGL_DEBUG_RENDERER
	assert( numPoints >= 3 );
	assert( ( numIndices >= 3 ) && ( 0 == ( numIndices % 3 ) ) );
	FUNCTION_PROFILER(gEnv->pSystem, PROFILE_RENDERER);
	for (size_t i = 0; i < numIndices; i+=3)
	{
		uint16 i0 = ind[i], i1 = ind[i+1], i2 = ind[i+2];
		assert(i0 < numPoints && i1 < numPoints && i2 < numPoints);
		m_triangles.push_back( STriangle( SPoint(v[i0], col), SPoint(v[i1], col), SPoint(v[i2], col) ) );
	}
#endif
}

void CNULLRenderAuxGeom::DrawTriangles(const Vec3 *v, uint32 numPoints, const uint16 *ind, uint32 numIndices, const ColorB *col)
{
#ifdef ENABLE_WGL_DEBUG_RENDERER
	assert( numPoints >= 3 );
	assert( ( numIndices >= 3 ) && ( 0 == ( numIndices % 3 ) ) );
	FUNCTION_PROFILER(gEnv->pSystem, PROFILE_RENDERER);
	for (size_t i = 0; i < numIndices; i+=3)
	{
		uint16 i0 = ind[i], i1 = ind[i+1], i2 = ind[i+2];
		assert(i0 < numPoints && i1 < numPoints && i2 < numPoints);
		m_triangles.push_back( STriangle( SPoint(v[i0], *col), SPoint(v[i1], *col), SPoint(v[i2], *col) ) );
	}
#endif
}

void CNULLRenderAuxGeom::DrawSphere(const Vec3 &pos, float radius, const ColorB &col, bool drawShaded)
{
#ifdef ENABLE_WGL_DEBUG_RENDERER
	m_spheres.push_back( SSphere( SPoint(pos, col), radius ) );
#endif
}

